package com.xn.monitor.core;

import com.xn.monitor.common.Constants;
import com.xn.monitor.common.MonitorUtils;
import com.xn.monitor.common.SerializeUtil;
import com.xn.monitor.entity.ServerEvent;
import com.xn.monitor.entity.ServerEventInfo;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.CreateMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Service;

/**
 * Created by yangshoujun on 2017/6/12 下午7:20.
 */
@Service
public class ApplicationMonitorRegister implements BeanFactoryPostProcessor {

    public static final String MONITOR_BASE_PATH = Constants.MONITOR_BASE_PATH;
    public static final String STATE_NODE = "state";
    public static final String STATE_PATH = MONITOR_BASE_PATH.concat("/").concat(STATE_NODE);

    private String applicationName = PropertyUtils.get("app.name");
    private String enviroment = PropertyUtils.get("app.model");

    private static ZkClient zkClient;
    private String nodePath;
    private String serverName;

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        zkClient = new ZkClient(PropertyUtils.get("zk.url"));
        initBasePath();
        registerNode();
        registerShutdownHook();
    }

    public void initBasePath() {

        if(!zkClient.exists(MONITOR_BASE_PATH)) {
            zkClient.create(MONITOR_BASE_PATH, MONITOR_BASE_PATH, CreateMode.PERSISTENT);
        }

        String envPath = MONITOR_BASE_PATH + "/" + enviroment;
        if(!zkClient.exists(envPath)) {
            zkClient.create(envPath, System.currentTimeMillis(), CreateMode.PERSISTENT);
        }

        if(!zkClient.exists(STATE_PATH)) {
            zkClient.create(STATE_PATH, "init", CreateMode.PERSISTENT);
        }

        String applicationPath = envPath + "/" + applicationName;
        if(!zkClient.exists(applicationPath)) {
            zkClient.create(applicationPath, applicationPath, CreateMode.PERSISTENT);
        }
    }

    public void registerNode() {
        Integer node = MonitorUtils.getPid();
        String envPath = MONITOR_BASE_PATH + "/" + enviroment;
        String host = MonitorUtils.getServerIpAddress();
        serverName = host + "-" + node;
        nodePath = envPath + "/" + applicationName + "/" + serverName;
        this.nodePath = nodePath;
        if(zkClient.exists(nodePath)) {
            return;
        }

        ServerEventInfo info = new ServerEventInfo();
        info.setApplicationName(applicationName);
        info.setEnv(enviroment);
        info.setEvent(ServerEvent.UP);
        info.setName(serverName);
        info.setTime(System.currentTimeMillis());
        byte[] serialized = SerializeUtil.serialize(info);
        zkClient.createEphemeral(nodePath, serialized);
        zkClient.writeData(STATE_PATH, serialized);

        zkClient.subscribeDataChanges(nodePath, new IZkDataListener() {
            public void handleDataChange(String dataPath, Object data) throws Exception {
            }

            public void handleDataDeleted(String dataPath) throws Exception {
                info.setEvent(ServerEvent.DOWN);
                info.setTime(System.currentTimeMillis());
                byte[] serialized = SerializeUtil.serialize(info);
                zkClient.writeData(STATE_PATH, serialized);
            }
        });

    }

    public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

            @Override
            public void run() {
                ServerEventInfo info = new ServerEventInfo();
                info.setApplicationName(applicationName);
                info.setEnv(enviroment);
                info.setEvent(ServerEvent.DOWN);
                info.setName(serverName);
                info.setTime(System.currentTimeMillis());
                byte[] serialized = SerializeUtil.serialize(info);
                zkClient.delete(nodePath);
                zkClient.writeData(STATE_PATH, serialized);
            }
        }));
    }

}
